import logging
import json
import txamqp.spec
from txamqp.client import TwistedDelegate
from txamqp.content import Content
from twisted.internet.protocol import ClientCreator
from twisted.internet import reactor
from txamqp.protocol import AMQClient
from twisted.internet.defer import inlineCallbacks
from dopplershift.core.utils.logger import DSLogging
from dopplershift.core.containers.mediator import getMediator
from dopplershift.core.utils.jsonserializer import dumps
from dopplershift.backend.mq import DSDefaultMQBackend
import traceback
logger = logging.getLogger(__name__)
from dopplershift.core.events import mq

class DSRabbitMQBackend(DSDefaultMQBackend):
    name = "rabbitmq"
    queue_name = 'chatrooms'
    exchange = 'chatservice' 
    routing_key = 'txamqp_chatroom'
    consumer_tag = 'testtag'
    def __init__(self, consumer=True, producer=False):
        self._delegate = TwistedDelegate()
        
        self.consumer = consumer
        self.producer = producer
    def configure(self, config):
        self._host = config.get('rabbitmq_host')
        self._port = int(config.get('rabbitmq_port'))
        self._user = config.get('rabbitmq_user')
        self._password = config.get('rabbitmq_password')
        self._virtual_host = config.get('rabbitmq_virtual_host')
        self.queue_name = config.get('rabbitmq_queue_name')
        self.exchange = config.get('rabbitmq_exchange_name')
        self.routing_key = config.get('rabbitmq_routing_key')
        self.consumer_tag = config.get('rabbitmq_consumer_tag')
        self._spec = txamqp.spec.load(config.get('rabbitmq_aqmp_specs'))
        try:
            self.run()
        except:
            traceback.print_exc()
        self._event_manager = getMediator().get("EventManager")
        self._event_manager.sign(mq.EVENT_MQ_MESSAGE_SEND, self.sendMessage)
    def run(self):
        d = ClientCreator(
                          reactor,
                          AMQClient,
                          delegate=self._delegate,
                          vhost=self._virtual_host,
                          spec=self._spec,
                          ).connectTCP(self._host, self._port)
        d.addCallback(self._connected)
    def _connected(self, conn):
        logger.debug("Connected to broker. try authenticate now")
        self._conn = conn
        d = self._conn.authenticate(self._user, self._password)
        d.addCallback(self._authenticated)
    @inlineCallbacks
    def _authenticated(self, result):
        logger.debug("Authenticated on broker. Try create channel")
        self._channel = yield self._conn.channel(2)
        logger.debug("Channel created. Opening it")
        yield self._channel.channel_open()
        logger.debug("Channel opened.")
        if self.consumer == True:
            self.start_consumer()
    def producing(self, data, routing_key=None):
        if routing_key == None: routing_key = self.routing_key
        msg = Content(data)
        msg['delivery mode'] = 2
        self._channel.basic_publish(exchange=self.exchange, content=msg, routing_key=routing_key)
        
    @inlineCallbacks    
    def start_consumer(self):
        yield self._channel.queue_declare(queue=self.queue_name, durable=True, exclusive=False, auto_delete=False)
        yield self._channel.exchange_declare(exchange=self.exchange, type="direct", durable=True, auto_delete=False)
        logger.debug("Declarated. Going to binding on queue %s with routing key %s"%(self.queue_name, self.routing_key))
        yield self._channel.queue_bind(queue=self.queue_name, exchange=self.exchange, routing_key=self.routing_key)
        logger.debug("Queue binded, start  consuming.")
        yield self._channel.basic_consume(queue=self.queue_name, no_ack=True, consumer_tag=self.consumer_tag)
        self.queue = yield self._conn.queue(self.consumer_tag)
        logger.debug("Queue connected, going waiting message")
        self.consuming()
    def sendMessage(self, msg):
        self.producing(self.dumpMessage(msg.body), msg.routing_key)
        
    def consuming(self):
        d = self.queue.get()
        d.addCallback(self._getMessage)
    def loadMessage(self, msg):
        return json.loads(msg)
    def dumpMessage(self, msg):
        return dumps(msg)
    def _getMessage(self, msg):
        logger.debug("get message from broker channel %s: %s"%(self._channel.id, msg.content.body))
        self.getMessage(self.loadMessage(msg))
    def getMessage(self, msg):
        self.consuming()
    @inlineCallbacks
    def stop(self):
        yield self._channel.basic_cancel(self.consumer_tag)
        yield self._channel.channel_close()
        chan = yield self._conn.channel(0)
        yield chan.connection_close()
        reactor.stop()